#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <errno.h>
#include <sys/wait.h>
#include <limits.h>
#include <unistd.h>
#include <string.h>
#include <syscall.h>
#include <sys/syscall.h> 

#define FAST_FUNC
#define CLOCK_MONOTONIC 1

#define CMD_START 0x01
#define CMD_PID   0x02
#define CMD_NULL_START 0x03
#define CMD_END   0x04

/* Information on the resources used by a child process.  */
typedef struct {
	int waitstatus;
    int command;
	struct rusage ru;
	unsigned elapsed_ms;	/* Wallclock time of process.  */
    unsigned start;
    unsigned skip;
    unsigned count;
} resource_t;

resource_t res;


#define UL unsigned long

pid_t gettid(void)
{
    return syscall(SYS_gettid);
}

static void get_mono(struct timespec *ts)
{
        if (syscall(__NR_clock_gettime, CLOCK_MONOTONIC, ts))
                perror("clock_gettime(MONOTONIC) failed");
}

unsigned long long FAST_FUNC monotonic_ns(void)
{
        struct timespec ts;
        get_mono(&ts);
        return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
}
unsigned long long FAST_FUNC monotonic_us(void)
{
	        struct timespec ts;
	        get_mono(&ts);
	        return ts.tv_sec * 1000000ULL + ts.tv_nsec/1000;
}
unsigned long long FAST_FUNC monotonic_ms(void)
{
	        struct timespec ts;
	        get_mono(&ts);
	        return ts.tv_sec * 1000ULL + ts.tv_nsec/1000000;
}
/* Wait for and fill in data on child process PID.
   Return 0 on error, 1 if ok.  */
/* pid_t is short on BSDI, so don't try to promote it.  */
static void resuse_end(pid_t pid, resource_t *resp)
{
	pid_t caught;

	/* Ignore signals, but don't ignore the children.  When wait3
	 * returns the child process, set the time the command finished. */
	while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) {
		if (caught == -1 && errno != EINTR) {
			perror("wait");
			return;
		}
	}
	resp->elapsed_ms = monotonic_ms() - resp->elapsed_ms;
}


void getNameByPid(pid_t pid, char *task_name) {
     char proc_pid_path[1024];
     char buf[1024];
 
     sprintf(proc_pid_path, "/proc/%d/status", pid);
     FILE* fp = fopen(proc_pid_path, "r");
     if(NULL != fp){
         if( fgets(buf, 1023, fp)== NULL ){
             fclose(fp);
         }
         fclose(fp);
         sscanf(buf, "%*s %s", task_name);
     }
}
static void run_command(char *const *cmd, resource_t *resp)
{
	pid_t pid,x8,x9,x10;
	void (*interrupt_signal)(int);
	void (*quit_signal)(int);

	resp->elapsed_ms = monotonic_ms();
	
#ifndef SAMEMMU
	pid = vfork();
#else
	pid = fork();
#endif
	if (pid == 0) {
        if ( (res.command == CMD_START) ||((res.command == CMD_NULL_START)) ) {
		    x8 = getppid();
		    x9 = getpid();
		    x10 = gettid();

#ifndef 	DEBUG
            /* first tell qemu the pid */
            __asm("ldr x8, %[input]"::[input]  "m" (x8));
            __asm("ldr x9, %[input]"::[input]  "m" (x9));
            __asm("ldr x10, %[input]"::[input]  "m" (x10));
            __asm ("svc #0x9877");

		    printf("QRTAG PPID[%d] PID[%d] TID[%d]]\n", x8,x9,x10);

            /* second set count */
            if ( res.count )
                x8 = res.count;
            else
                x8 = 0;
            if ( res.skip )
                x9 = res.skip;
            else
                x9 = 0;
		     __asm ("svc #0x9876");
#endif
        }
		/* Child */
        if ( (res.command == CMD_START) && (cmd[0] != NULL)) {
	        execvp(cmd[0], &cmd[0]);
        }
        exit(1);
	}
    if ( res.command == CMD_START) {
        interrupt_signal = signal(SIGINT, SIG_IGN);
        quit_signal = signal(SIGQUIT, SIG_IGN);

        resuse_end(pid, resp);

        /* Re-enable signals.  */
        signal(SIGINT, interrupt_signal);
        signal(SIGQUIT, quit_signal);
    }
}

int common_exit(int argc, char **argv)
{
	int ex;
	
    if ( res.command == CMD_END)
        goto out;

	run_command(argv, &res);

    if ( res.command == CMD_START) {
        ex = WEXITSTATUS(res.waitstatus);
        if (WIFSIGNALED(res.waitstatus))
            ex = WTERMSIG(res.waitstatus);
    }
	
	fflush(stdout);
out:
	printf("PPI qrtag common exit\n");
#ifndef 	DEBUG
    if ( res.command == CMD_START) {
	    __asm ("svc #0x9875");
    }
#endif
}
void usage(void)
{
    printf("qrtag [-s <num> ] [-j <num>] [-c <num>] <program>\n");
    printf("\t or qrtag <-e > \n");
    printf("\t -s:  start num \n");
    printf("\t -j:  skip num \n");
    printf("\t -c:  totoal num \n");
    printf("\t -p:  run a program with parameters \n");

    exit(1);

}
int argv_parser( char const  * cmd_str, char ***argv_in){
    int str_symbol = 0;
    int argc = 0;
    int i = 0;
    int j = 0;
    char **argv = *argv_in;
    argv[argc] = (char *)malloc(sizeof(char)*50);
    while(cmd_str[j] != '\0' && cmd_str[j] != '\n'&& cmd_str[j] != NULL){
        if(cmd_str[j] == '"'){
	    str_symbol = (str_symbol +1)%2;
	    j++;
	}	
	if(str_symbol == 1){
	    //argv[argc]
	    size_t len;
	    argv[argc][i] =cmd_str[j];
	    j++;
	    i++;
	    continue;
	}
	if(cmd_str[j] != ' '){
	    argv[argc][i] =cmd_str[j];
	    j++;
	    i++;
	}else{
	   // argv[argc][i] ='\0';
	    i = 0;
	    ++argc;
	    argv[argc] = (char *)malloc(sizeof(char)*50);
	    while(cmd_str[j] == ' '){
	        j++;
	    }
	}
    }
    if(i !=0) ++argc;
#ifdef DEBUG_P
    printf("argc = %d \n", argc);
    for(int m = 0; m < argc; m ++){
        printf("%s %d\n", argv[m], strlen(argv[m]));
    }
#endif
    //*argv_in = argv;
    return argc; 
}
int main(int argc, char **argv)
{
	/* ARGC = 2
	 * qrtag -s
	 * qrtag -e
	 * qrtag ./app
	 */
    int ch;
    char *argvp;
    int child_argc = 0;
    res.command = CMD_START;
    char **child_argv;
    printf("argc  %d \n", argc);
    while ((ch = getopt(argc, argv, ":esc:j:p:")) != -1) {
        switch (ch) {
        case 's':
                printf("PPI qrtag -s!\n");
                res.command = CMD_NULL_START;
                break;
        case 'e':
                printf("PPI qrtag -e!\n");
                res.command = CMD_END;
                break;
        case 'c':
			    res.count = atoi(optarg);
                if ( res.count <= 0 )  {
                    usage();
                }
                break;
        case 'j':
			    res.skip = atoi(optarg);
                if ( res.skip <= 0 )  {
                    usage();
                }
                break;
	case 'p':
	        argvp= optarg;
		child_argv = (char**)malloc(30*sizeof(char *));
		child_argc = argv_parser(argvp, &child_argv);
		break;
        dafault:
                res.command = CMD_START;
                break;
        }

    }

    if (  res.command < CMD_START ||  res.command > CMD_END)
        usage();
#if 0
    int i;
    printf("-----------optind[%d] optarg[%s]-------\n",optind,argv[optind]);
    for(i=0;i<argc;i++)
    {
            printf("%s\n",argv[i]);
    }
    printf("--------------------------\n");
#endif
	//common_exit((argc-optind+1),&argv[optind-1]);
	common_exit(child_argc,child_argv);

    return 0;
}

